/*
 * Utils.cpp
 *
 *  Created on: Apr 20, 2012
 *      Author: thuvh
 */

#include <fstream>
#include <sstream>

#include <cstdlib>

#include "Utils.h"

#include "Symbol.h"
#include "NonTerminal.h"
#include "Terminal.h"

std::vector<std::string> tokenize(std::string inpStr, std::string delim) {
	std::vector<std::string> retVal;
	std::size_t lenOfStr = inpStr.size();

	for (std::size_t bIdx = 0; bIdx < lenOfStr;) {
		std::size_t fIdx = inpStr.find_first_of(delim, bIdx);

		if (fIdx == std::string::npos) {
			fIdx = lenOfStr;
		}

		std::string cand = inpStr.substr(bIdx, fIdx - bIdx);
		if (!cand.empty()) {
			retVal.push_back(cand);
		}

		bIdx = fIdx + 1;
	}

	return retVal;
}
/**
 * input $AB
 * input $AB[H]
 * input $AB["the"]
 */
Symbol * getSymbol(std::string inpStr, int start, int end) {
	Symbol *retVal = NULL;
//	std::cout << inpStr << std::endl;
	std::vector<std::string> element = tokenize(inpStr, "$[]\"");
	std::size_t numOfElement = element.size();

	switch (numOfElement) {
	case NON_TERMINAL:
		retVal = new NonTerminal(element[0], start, end);
		break;
	case TERMINAL_OR_HEAD_NON_TERMINAL:
		if (element[1] == "H") {
			retVal = new NonTerminal(element[0], true, start, end);
		} else {
			retVal = new Terminal(element[0], start, end, element[1]);
		}

		break;

	case ERROR:
	default:
		for (size_t ci = 0; ci < numOfElement; ci++) {
			std::cerr << "ci = " << ci << ", tok = '" << element[ci] << "'"
					<< std::endl;
		}
		std::cerr << "loi: '" << inpStr << "', num of element = "
				<< numOfElement << std::endl;
		exit(1);
		break;
	}

	return retVal;
}

std::vector<int> getIntVect(std::string inpStr) {
	std::vector<int> retVal;

	std::vector<std::string> toks = tokenize(inpStr);
	std::size_t numOfToks = toks.size();

	for (std::size_t ci = 0; ci < numOfToks; ci++) {
		retVal.push_back(atoi(toks[ci].c_str()));
	}

	return retVal;
}

Symbol * loadRuleFromFile(std::string filename) {
//	std::vector<Symbol *> retVal;
	Symbol * rules = new NonTerminal ("Rules");

	std::ifstream fi(filename.c_str());

	if (!fi.good()) {
		std::cerr << "Error in open file: " << filename << std::endl;
	} else {
		bool flag = true;
		while (flag) {
			std::string firstLine;
			std::string secondLine;

			if (!fi.good()) {
				flag = false;
			}

			getline(fi, firstLine, '\n');
			getline(fi, secondLine, '\n');
			if (firstLine.empty() || secondLine.empty()) {
				continue;
			}

			if (firstLine[firstLine.length() - 1] == '\r') {
				firstLine = firstLine.substr(0, firstLine.length() - 1);
			}

			if (secondLine[secondLine.length() - 1] == '\r') {
				secondLine = secondLine.substr(0, secondLine.length() - 1);
			}

			std::vector<std::string> toks = tokenize(firstLine, " :=");
			std::string lhsTok = toks[0];
			Symbol * root = getSymbol(lhsTok, 0, toks.size() - 1);

			if (root == NULL) {
				std::cerr << "error in parse line: " << firstLine << ", "
						<< secondLine << std::endl;
				exit(1);
			}

			std::size_t numOfRhs = toks.size();
			for (std::size_t ci = 1; ci < numOfRhs; ci++) {
				std::string inpStr = toks[ci];
				Symbol *node = getSymbol(inpStr, ci - 1, ci);
				root->nonTerminal()->push_back(node);

			}

			std::vector<int> order = getIntVect(secondLine);
			root->nonTerminal()->setOrdered(order);

//			retVal.push_back(root);
			rules->nonTerminal()->push_back(root);
		}
	}
	return rules;
}

std::vector<Symbol *> loadSentence(std::vector<std::string> words,
		std::vector<std::string> tags, std::vector<std::string> chunks) {
	std::vector<Symbol *> retVal;

	if (words.size() == tags.size() && tags.size() == chunks.size()) {
		std::vector<std::size_t> segments = segment(chunks);
		std::size_t numOfSegments = segments.size();

		for (std::size_t ci = 0; ci < numOfSegments - 1; ci++) {
			std::size_t start = segments[ci];
			std::size_t end = segments[ci + 1];

			std::string function = chunks[start].substr(2);
			Symbol * root = new NonTerminal(function, start, end);
			for (size_t cj = start; cj < end; ci++) {
				Symbol * word = new Terminal(tags[cj], cj, cj + 1, words[cj]);
				root->nonTerminal()->push_back(word);
			}

			retVal.push_back(root);

		}
	} else {
		std::cerr << "size different check your input" << std::endl;
		exit(1);
	}

	return retVal;
}

std::vector<std::size_t> segment(std::vector<std::string> chunks) {
	std::vector<std::size_t> retVal;

	std::size_t lenOfSeg = chunks.size();

	for (size_t ci = 0; ci < lenOfSeg; ci++) {
		std::string tmp = chunks[ci];
		if (tmp[0] == 'B') {
			retVal.push_back(ci);
		} else if (tmp.length() == 1 && tmp == "O") {
			retVal.push_back(ci);
		}
	}
	retVal.push_back(lenOfSeg - 1); //end of string

	return retVal;
}

std::vector<std::vector<Symbol *> > loadSentences(std::string filename) {
	std::vector<std::vector<Symbol *> > sentences;

	std::ifstream fi(filename.c_str());

	std::vector<std::string> words;
	std::vector<std::string> tags;
	std::vector<std::string> chunks;

	std::string line;

	if (fi.good() == false) {
		std::cerr << "error in open file " << filename << std::endl;
		exit(1);
	}

	bool isContinue = true;
	while (isContinue) {

		getline(fi, line, '\n');

		isContinue = fi.good();
		std::cout << "line: " << line << ", " << isContinue << std::endl;
		if (line.empty() || line[0] == ' ') {
			std::vector<Symbol *> sentence = loadSentence(words, tags, chunks);
			sentences.push_back(sentence);
			words.clear();
			tags.clear();
			chunks.clear();
			std::cout << "he he" << std::endl;
		} else {
			std::vector<std::string> toks = tokenize(line, " \t");
			if (toks.size() != NUM_OF_ELE_IN_LINE) {
				std::cerr << " diff with element in '" << line << "'"
						<< std::endl;
				exit(1);
			}
			std::cout << toks[0] << " " << toks[1] << " " << toks[2]
					<< std::endl;
			words.push_back(toks[0]);
			tags.push_back(toks[1]);
			chunks.push_back(toks[2]);
		}
	}

	fi.close();
	return sentences;
}

void loadFile(std::string filename, Symbol * rules) {
	std::ifstream fi(filename.c_str());

	Symbol *sentence = new NonTerminal();
	std::string line;
	int wIdx = 0;
	std::string prev;
	std::string cur;

	while (fi.good()) {
		getline(fi, line, '\n');

		if (line.empty()) {
//			std::cout << sentence->toString() << std::endl;
			std::size_t numOfRule = rules->nonTerminal()->sizeOfChildren();
			for (std::size_t ruleIdx = 0; ruleIdx < numOfRule; ruleIdx++) {
				bool tmp = processSentence(sentence,
						rules->nonTerminal()->getChildrenAt(ruleIdx));
				if (tmp) {
					break;
				}

			}

			std::cout << sentence->getSentence() << std::endl;

			sentence = new NonTerminal();
			wIdx = 0;
		} else {
			std::vector<std::string> toks = tokenize(line, " \t");

			Terminal * term = new Terminal(toks[1], wIdx, wIdx + 1, toks[0]);
//			std::cout<< term->toString() << std::endl;
			if (toks[2] != "O") {
				cur = toks[2].substr(2);
			} else {
				cur = toks[2];
			}

			Symbol * ele = NULL;

			if (toks[2] == "O") {
				ele = new NonTerminal(cur, wIdx, wIdx + 1);
				ele->nonTerminal()->push_back(term);
				sentence->nonTerminal()->push_back(ele);

			} else if (toks[2][0] == 'B') {
				Symbol * ele = new NonTerminal(cur, wIdx, wIdx + 1);
				ele->nonTerminal()->push_back(term);
				sentence->nonTerminal()->push_back(ele);
//				std::cout << sentence->toString() << std::endl;
//				std::cout << ele->toString() << std::endl;
			} else if (toks[2][0] == 'I') {

				ele = sentence->nonTerminal()->getChildrenAt(
						sentence->nonTerminal()->sizeOfChildren() - 1);
//
				ele->nonTerminal()->push_back(term);
//				sentence->nonTerminal()->setElementAt(term,
//						sentence->nonTerminal()->sizeOfChildren() - 1);
			} else {
				std::cerr << "error go here: '" << line << "'" << std::endl;
				exit(1);
			}
		}

		wIdx++;
	}

	fi.close();
}

bool isMatch(Symbol * chunk, Symbol * rule) {
	bool retVal = false;

	//chunk is root node so that:
	//first is non terminal
	if (chunk->isNonTerminal()) {
		//check tag between them
//		std::cout << "chunk is satified" << std::endl;
		retVal = chunk->getSymbol() == rule->getSymbol();
		if (retVal) {
			//same size
			retVal = chunk->nonTerminal()->sizeOfChildren()
					== rule->nonTerminal()->sizeOfChildren();
//			std::cout << "chunk is satified" << std::endl;
			if (retVal) {
				std::size_t numOfChild = rule->nonTerminal()->sizeOfChildren();
//				std::cout << "chunk is satified" << std::endl;
				for (std::size_t ci = 0; ci < numOfChild; ci++) {
					//check each type
					Symbol * r = rule->nonTerminal()->getChildrenAt(ci);
//					std::cout << "chunk is satified" << std::endl;
					//neu note con cua rule la terminal
					//neu note con cua rule la nonterminal
					//otherwise error
					if (r->isTerminal()) {
						retVal = r->terminal()->isMatch(
								chunk->nonTerminal()->getChildrenAt(ci));

						if (!retVal) {
							break;
						}
					} else if (r->isNonTerminal()) {
						retVal =
								chunk->nonTerminal()->getChildrenAt(ci)->getSymbol()
										== r->getSymbol();
						if (!retVal) {
							break;
						}
					} else {
						std::cerr << "error in compare chunk: "
								<< chunk->toString() << ", rule: "
								<< rule->toString() << std::endl;
						exit(1);
					}
				}
			}
		}
	}

	return retVal;
}

bool processSentence(Symbol * sentence, Symbol * rule) {
	bool retVal = false;

	std::size_t numOfChunk = sentence->nonTerminal()->sizeOfChildren();

	for (std::size_t ci = 0; ci < numOfChunk; ci++) {
		retVal = getReorderChunk(sentence->nonTerminal()->getChildrenAt(ci),
				rule);
		if (retVal) {
			break;
		}
	}

	return retVal;
}

bool getReorderChunk(Symbol * chunk, Symbol * rule) {
	bool retVal = false;

	if (chunk->isNonTerminal()) {
		retVal = isMatch(chunk, rule);
		if (retVal) {
//			std::vector<int> ordered = rule->nonTerminal()->getOrdered();
//			std::size_t sizeOfOrdered = ordered.size();
			chunk->nonTerminal()->setOrdered(rule->nonTerminal()->getOrdered());
		}
	}

	return retVal;
}
